package io.github.fb513.transport.server;

import io.github.fb513.transport.command.CommandChain;
import io.github.fb513.transport.config.BootstrapConfig;
import io.github.fb513.transport.config.ImConfig;
import io.github.fb513.transport.handler.ServerHandler;
import io.github.fb513.transport.handler.WebSocketMessageDecoder;
import io.github.fb513.transport.handler.WebSocketMessageEncoder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;

public class WebSocketServer {

    private final ServerBootstrap bootstrap;
    private final BootstrapConfig bootstrapConfig;

    public WebSocketServer(BootstrapConfig bootstrapConfig, CommandChain commandChain) {
        this.bootstrapConfig = bootstrapConfig;

        var mainGroup = new NioEventLoopGroup(1);
        var workGroup = new NioEventLoopGroup();
        bootstrap = new ServerBootstrap();
        bootstrap.group(mainGroup, workGroup).channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 10240) // 服务端可连接队列大小
                .option(ChannelOption.SO_REUSEADDR, true) // 参数表示允许重复使用本地地址和端口
                .childOption(ChannelOption.TCP_NODELAY, true) // 是否禁用Nagle算法 简单点说是否批量发送数据 true关闭 false开启。 开启的话可以减少一定的网络开销，但影响消息实时性
                .childOption(ChannelOption.SO_KEEPALIVE, true) // 保活开关2h没有数据服务端会发送心跳包
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        var pipeline = socketChannel.pipeline();
                        pipeline.addLast(new HttpServerCodec())
                                .addLast(new ChunkedWriteHandler())
                                .addLast(new HttpObjectAggregator(65535))
                                .addLast(new WebSocketServerProtocolHandler("/ws"))

                                .addLast(new WebSocketMessageDecoder())
                                .addLast(new WebSocketMessageEncoder())
                                .addLast(new IdleStateHandler(0, 0,
                                        bootstrapConfig.getIm().getIdleTimeSeconds()))
                                .addLast(new ServerHandler(commandChain))
                                ;
                    }
                });
    }

    public void start(){
        ImConfig imConfig = bootstrapConfig.getIm();
        bootstrap.bind(imConfig.getPort());
    }
}
